package com.example.springboot.utils.encrypt;

import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Administrator on 2016/9/12 0012.
 */
@Slf4j
public class RsaUtil {
    /**
     * RSA:加密算法
     */
    public static final String KEY_ALGORITHM = "RSA";
    public static final String charset = "UTF8";
    public static final String KEY_ALGORITHM_RSA_NONE = "RSA/ECB/PKCS1Padding";
    /**
     * SHA1WithRSA:用SHA算法进行签名，用RSA算法进行加密
     */
    public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
    /**
     * 公钥
     */
    public static final String RSAPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCaiwcznX017IXlx74FQ72HvqGURZMvlpjkirGmdDF8DzcO6xsfQnpHHRL4Wi0YRmSjA3TC15QoS37Bas6bMnDpj6Xjvs/o4p6hf1Y3HRJK9lbLQkSBop6B+8mjNLNnYr2eEQsDofGv/vA24yr/Zg9FvsDBUAl/9FPtnHeKIk6uAQIDAQAB";
    /**
     * 私钥
     */
    public static final String RSAPrivateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJqLBzOdfTXsheXHvgVDvYe+oZRFky+WmOSKsaZ0MXwPNw7rGx9CekcdEvhaLRhGZKMDdMLXlChLfsFqzpsycOmPpeO+z+jinqF/VjcdEkr2VstCRIGinoH7yaM0s2divZ4RCwOh8a/+8DbjKv9mD0W+wMFQCX/0U+2cd4oiTq4BAgMBAAECgYB9skaRPK7yhQYC9osfTRGCrxJMvqoQMrT4bbZAZJE7DkUB5UeSDUfXP1aGmRHEhLyludHmB+GBRKY8OtsAb+5VQhinvDM2ihCpvoFMNULCb1cwrTdesN22l0yqEYBK6scYuQ4CZqIIWltiz7Xc1Tc2xUTeiNNQBcmdNzHk/0hmYQJBAOfj2CAGi+IfRNPOpfKIULaag+DOB25XKAW/z12Nfs6ryYKiqQdusYnWeRuBY7YvCLMmfYPq9KF2EM4eyGIB2BsCQQCqnHXyVp/n/RiXf17NkJZh4lDPp4W6u0VENbKnQ5Y6J49SF7D9ikKsGgYcr20k7PmiFKgCvstxev2TTDYfhSwTAkBjanJhII1eL2uxpvnMZha8grghbBMRgjo9ZMOBZ0VDU7D72cK/1fw1jQa0/R8Dn1yKeej74oJoqpZ/2ACuy/2bAkBWEynOYbW7LQ3DIReLTm8QY0Wx/iqR0vlXOGAx0v9vx5WfLSS2RZLtkROUVU0TXBt1kkZRXED3t71HGyQTMvTRAkBYA7R5drgCkrG2X+45t7nW5QSv91/QQNuapnCP1b3sja+MBUrzD5oKAh9PbvNcJYcg4JIRGK0tZgZy+Pp+znrq";
    /**
     * 块加密的大小，是不要太大，否则效率会低
     */
    private static final int KEY_SIZE = 1024;
    /**
     * 获取公钥的key
     */
    private static final String PUBLIC_KEY = "RSAPublicKey";
    /**
     * 获取私钥的key
     */
    private static final String PRIVATE_KEY = "RSAPrivateKey";
    /**
     * RSA最大加密明文大小
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
    /**
     * RSA最大解密密文大小
     */
    private static final int MAX_DECRYPT_BLOCK = 128;
    /**
     * * 生成密钥对 *
     *
     * @return KeyPair *
     * @throws Exception
     */
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(KEY_SIZE, new SecureRandom());
        KeyPair keyPair = keyPairGen.generateKeyPair();
        return keyPair;
    }

    /**
     * * 生成公钥 *
     *
     * @param keyPair *
     * @return RSAPublicKey *
     * @throws Exception
     */
    public static String generateRSAPublicKey(KeyPair keyPair) throws Exception {

        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        return Base64Util.encode(publicKey.getEncoded());
    }

    /**
     * * 生成私钥 *
     *
     * @param keyPair *
     * @return RSAPublicKey *
     * @throws Exception
     */
    public static String generateRSAPrivateKey(KeyPair keyPair) throws Exception {

        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        return Base64Util.encode(privateKey.getEncoded());
    }

    /**
     * * 生成公私钥对 *
     *
     * @return Map *
     * @throws Exception
     */
    public static Map<String, Object> generateRSAKey() throws Exception {
        KeyPair keyPair = generateKeyPair();
        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, generateRSAPublicKey(keyPair));
        keyMap.put(PRIVATE_KEY, generateRSAPrivateKey(keyPair));
        return keyMap;
    }

    /**
     * 用私钥对信息生成数字签名
     *
     * @param content    待签名数据
     * @param privateKey 私钥(BASE64编码)
     * @param charset    编码格式
     * @return 签名值
     */
    public static String sign(String content, String privateKey, String charset) throws Exception {
        PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64Util.decode(privateKey));
        KeyFactory keyf = KeyFactory.getInstance(KEY_ALGORITHM);
        PrivateKey priKey = keyf.generatePrivate(priPKCS8);

        Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
        signature.initSign(priKey);
        signature.update(content.getBytes(charset));
        byte[] signed = signature.sign();
        return Base64Util.encode(signed);
    }


    /**
     * 用私钥对信息生成数字签名
     *
     * @param content    待签名数据
     * @param privateKey 私钥(BASE64编码)
     * @param charset    编码格式
     * @return 签名值
     */
    public static String sign(String content, PrivateKey privateKey, String charset) throws Exception {
        Signature signature = Signature.getInstance(SIGN_ALGORITHMS);
        signature.initSign(privateKey);
        signature.update(content.getBytes(charset));
        byte[] signed = signature.sign();
        return Base64Util.encode(signed);
    }

    /**
     * 用公钥对信息RSA验签名检查
     *
     * @param content   待签名数据
     * @param sign      签名值
     * @param publicKey 公钥
     * @param charset   编码格式
     * @return 布尔值
     */
    public static boolean verify(String content, String sign, String publicKey, String charset) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        byte[] encodedKey = Base64Util.decode(publicKey);
        PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));

        Signature signature = Signature.getInstance(SIGN_ALGORITHMS);

        signature.initVerify(pubKey);
        signature.update(content.getBytes(charset));

        return signature.verify(Base64Util.decode(sign));
    }

    /**
     * 用公钥对信息RSA验签名检查
     *
     * @param content   待签名数据
     * @param sign      签名值
     * @param publicKey 公钥
     * @param charset   编码格式
     * @return 布尔值
     */
    public static boolean verify(String content, String sign, PublicKey publicKey, String charset) throws Exception {

        Signature signature = Signature.getInstance(SIGN_ALGORITHMS);

        signature.initVerify(publicKey);
        signature.update(content.getBytes(charset));

        return signature.verify(Base64Util.decode(sign));
    }

    /**
     * 使用公钥RSA加密
     *
     * @param content   明文
     * @param publicKey 公钥
     * @param charset   编码格式
     * @return 加密后的字符串
     */
    public static String encrypt(String content, String publicKey, String charset) throws Exception {
        String str;
        ByteArrayOutputStream writer = null;
        try {
            PublicKey pubKey = getPublicKey(publicKey);

            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_RSA_NONE);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);

            // 设置编码格式
            InputStream ins = new ByteArrayInputStream(content.getBytes(charset));
            writer = new ByteArrayOutputStream();
            //rsa加密的字节大小最多是117，将需要加密的内容，按117位拆开加密
            byte[] buf = new byte[MAX_ENCRYPT_BLOCK];
            int bufl;
            while ((bufl = ins.read(buf)) != -1) {
                byte[] block;

                if (buf.length == bufl) {
                    block = buf;
                } else {
                    block = new byte[bufl];
                    for (int i = 0; i < bufl; i++) {
                        block[i] = buf[i];
                    }
                }
                writer.write(cipher.doFinal(block));
            }
            str = new String(Base64Util.encode(writer.toByteArray()));
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    log.error("异常[{}]", e.toString());
                }
                writer = null;
            }
        }
        return str;
    }

    /**
     * 使用默认公钥RSA加密
     *
     * @param content 明文
     * @return 加密后的字符串
     */
    public static String encrypt(String content) throws Exception {
        return encrypt(content, RSAPublicKey, charset);
    }

    /**
     * 使用公钥RSA加密
     *
     * @param content   明文
     * @param publicKey 公钥
     * @param charset   编码格式
     * @return 加密后的字符串
     */
    public static String encrypt(String content, PublicKey publicKey, String charset) throws Exception {
        String str;
        ByteArrayOutputStream writer = null;
        try {

            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_RSA_NONE);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);

            // 设置编码格式
            InputStream ins = new ByteArrayInputStream(content.getBytes(charset));
            writer = new ByteArrayOutputStream();
            //rsa加密的字节大小最多是117，将需要加密的内容，按117位拆开加密
            byte[] buf = new byte[MAX_ENCRYPT_BLOCK];
            int bufl;
            while ((bufl = ins.read(buf)) != -1) {
                byte[] block;

                if (buf.length == bufl) {
                    block = buf;
                } else {
                    block = new byte[bufl];
                    for (int i = 0; i < bufl; i++) {
                        block[i] = buf[i];
                    }
                }
                writer.write(cipher.doFinal(block));
            }
            str = new String(Base64Util.encode(writer.toByteArray()));
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    log.error("异常[{}]", e.toString());
                }
                writer = null;
            }
        }
        return str;
    }

    /**
     * 使用私钥RSA解密
     *
     * @param content    密文
     * @param privateKey 商户私钥
     * @param charset    编码格式
     * @return 解密后的字符串
     */
    public static String decrypt(String content, String privateKey, String charset) throws Exception {
        PrivateKey prikey = getPrivateKey(privateKey);
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, prikey);
        InputStream ins = new ByteArrayInputStream(Base64Util.decode(content));
        ByteArrayOutputStream writer = new ByteArrayOutputStream();
        // rsa解密的字节大小最多是128，将需要解密的内容，按128位拆开解密
        byte[] buf = new byte[MAX_DECRYPT_BLOCK];
        int bufl;

        while ((bufl = ins.read(buf)) != -1) {
            byte[] block;

            if (buf.length == bufl) {
                block = buf;
            } else {
                block = new byte[bufl];
                for (int i = 0; i < bufl; i++) {
                    block[i] = buf[i];
                }
            }
            writer.write(cipher.doFinal(block));
        }
        return new String(writer.toByteArray(), charset);
    }

    /**
     * 使用私钥RSA解密
     *
     * @param content 密文
     * @return 解密后的字符串
     */
    public static String decrypt(String content) throws Exception {
        return decrypt(content, RSAPrivateKey, charset);
    }

    /**
     * 使用私钥RSA解密
     *
     * @param content    密文
     * @param privateKey 商户私钥
     * @param charset    编码格式
     * @return 解密后的字符串
     */
    public static String decrypt(String content, PrivateKey privateKey, String charset) throws Exception {
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        InputStream ins = new ByteArrayInputStream(Base64Util.decode(content));
        ByteArrayOutputStream writer = new ByteArrayOutputStream();
        // rsa解密的字节大小最多是128，将需要解密的内容，按128位拆开解密
        byte[] buf = new byte[MAX_DECRYPT_BLOCK];
        int bufl;

        while ((bufl = ins.read(buf)) != -1) {
            byte[] block;

            if (buf.length == bufl) {
                block = buf;
            } else {
                block = new byte[bufl];
                for (int i = 0; i < bufl; i++) {
                    block[i] = buf[i];
                }
            }
            writer.write(cipher.doFinal(block));
        }
        return new String(writer.toByteArray(), charset);
    }

    /**
     * 得到私钥
     *
     * @param key 密钥字符串（经过base64编码）
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {

        byte[] keyBytes = Base64Util.decode(key);

        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);

        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

        return privateKey;
    }

    /**
     * 得到公钥
     *
     * @param key 加密字符串（经过base64解码）
     * @throws Exception
     */
    public static PublicKey getPublicKey(String key) throws Exception {

        byte[] keyBytes;

        keyBytes = Base64Util.decode(key);

        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

        PublicKey publicKey = keyFactory.generatePublic(keySpec);

        return publicKey;
    }

    public static void main(String[] args) throws Exception {
//        Map<String, Object> map = generateRSAKey();
//        for (String key : map.keySet()) {
//            System.out.println("密钥的key：" + key);
//            System.out.println("密钥的value：" + map.get(key));
//        }
        Map<String, Object> map = new HashMap<>();
        map.put("group", "发送到");
        map.put("name", "namea是");
        map.put("value", 23);
        map.put("sign", "7B58B5FCA1CE44946585E8AAB17E3881");
        for (String key : map.keySet()) {
            map.put(key, encrypt(map.get(key).toString(), RSAPublicKey, charset));
        }
        log.debug(map.toString());

        Map<String, Object> mapTemp = new HashMap<>();
        for (String key : map.keySet()) {
            mapTemp.put(key, decrypt(map.get(key).toString(), RSAPrivateKey, charset));
        }
        log.debug(mapTemp.toString());


    }

}
